package cn.com.zj.rpc.provider.handler;

import cn.com.zj.rpc.common.RpcRequest;
import cn.com.zj.rpc.common.RpcResponse;
import cn.com.zj.rpc.provider.anno.MyRpcService;
import com.alibaba.fastjson.JSONObject;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.springframework.beans.BeansException;
import org.springframework.cglib.reflect.FastClass;
import org.springframework.cglib.reflect.FastMethod;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @ClassName MyRpcServerHandler
 * @Description 自定义rpc服务端业务处理类
 * 逻辑：
 * 1.将所有被@MyRpcService注解标注的bean缓存:实现ApplicationContextAware接口
 * 2.接收客户端发送过来的消息
 * 3.获取需要调用的接口名、方法名、参数类型及参数值
 * 4.根据类名咋缓存中获取对应接口
 * 5.通过反射调用对应方法获得返回结果
 * 6.将返回结果发送给客户端
 *
 * @Author ZikZ227412
 * @DATE 2021/12/9 7:58
 * @Version 1.0
 */
@Component
@ChannelHandler.Sharable
public class MyRpcServerHandler extends SimpleChannelInboundHandler<String> implements ApplicationContextAware {

    private static final Map<String,Object> service_instance_map = new ConcurrentHashMap();

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
        //将json消息转换为RpcRequest
        RpcRequest rpcRequest = JSONObject.parseObject(s, RpcRequest.class);
        RpcResponse rpcResponse = new RpcResponse();
        rpcResponse.setRequestId(rpcRequest.getRequestId());
        try {
            rpcResponse.setResult(handler(rpcRequest));
        } catch (InvocationTargetException e) {
            e.printStackTrace();
            rpcResponse.setError(e.getMessage());
        }
        //将返回结果发送给客户端
        channelHandlerContext.writeAndFlush(JSONObject.toJSONString(rpcResponse));
    }



    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, Object> serviceMap = applicationContext.getBeansWithAnnotation(MyRpcService.class);
        if(!CollectionUtils.isEmpty(serviceMap)){
            Set<Map.Entry<String, Object>> entries = serviceMap.entrySet();
            for (Map.Entry<String, Object> entry : entries) {
                Object serviceBean = entry.getValue();
                //判断被MyRpcService注解修饰的类是否实现接口
                if(serviceBean.getClass().getInterfaces().length == 0){
                    throw new RuntimeException("服务必须实现接口");
                }
                //默认取第一个接口作为缓存bean的名称
                String name = serviceBean.getClass().getInterfaces()[0].getName();
                service_instance_map.put(name,serviceBean);

            }
        }
    }

    private Object handler(RpcRequest rpcRequest) throws InvocationTargetException {
        //获取被调用接口名称
        String className = rpcRequest.getClassName();
        //获取方法名称
        String methodName = rpcRequest.getMethodName();
        //获取被调用方法参数值数组
        Object[] parameters = rpcRequest.getParameters();
        //获取被调用方法参数类型数组
        Class<?>[] parameterTypes = rpcRequest.getParameterTypes();


        //在缓存中获取对应接口
        Object serviceBean = service_instance_map.get(className);
        Class<?> beanClass = serviceBean.getClass();

        //通过反射调用bean的方法 通过cglib调用
        FastClass fastClass = FastClass.create(beanClass);
        FastMethod method = fastClass.getMethod(methodName, parameterTypes);
        Object result = method.invoke(serviceBean, parameters);
        return result;

    }
}
